import { Dep } from "./dep";

/*
new Watcher(vm, ()=>vm.count, (value, old)=>{
    console.log(value, old);
});
当 vm.count 发生改变的时候， 就会打印新值和老值
*/
import {queueWatcher} from './queueWatcher'
let stack = [];
function pushTarget(watcher){
    stack.push(watcher);
    Dep.target = watcher;
}
function popTarget(){
    stack.pop();
    Dep.target = stack[stack.length-1];
}
function parsePath(path){
    const segments = path.split('.')
    return function (vm){
        for (let i=0; i<segments.length; i++){
            vm = vm[segments[i]]
            if (!vm)return;
        }
        return vm;
    }
}
let id = 0;
export class Watcher{
    constructor(vm, fnOrExpression, cb=function(){}, options){
        this.id = id++;
        this.vm = vm;
        if (typeof fnOrExpression !== "function"){
            this.fn = parsePath(fnOrExpression);
        }else{
            this.fn = fnOrExpression;
        }
        this.cb = cb;
        this.dirty = this.lazy = false;
        this.newSubsIds = new Set();
        this.newSubs = [];
        if (options){
            this.lazy = !!options.lazy;
            this.dirty = this.lazy
        }
        this.value = this.lazy ? undefined : this.get();
    }
    evaluate(){
        this.get();
        this.dirty = false;
    }
    get(){
        // Dep.target = this;
        // 父子组件, 子组件会把父覆盖, 然后父得不到结果了。
        pushTarget(this);
        // 执行 vm.count, 这样会进入 count的getter函数, 我们可以在getter收集到这个watcher
        // 当 设置 vm.count时候, 会把收集的watcher进行触发(调用watcher的update方法)
        const value = this.fn.call(this.vm, this.vm);
        this.value = value;
        // Dep.target = null;
        popTarget();
        return value;
    }
    addDep(dep){
        // 当 watcher 记住 dep
        const depId = dep.id;
        if (!this.newSubsIds.has(depId)){
            this.newSubsIds.add(depId);
            this.newSubs.push(dep);
        }
        // 让 dep也记住当前 watcher
        dep.addDep(this);
    }
    depend(){
        // 这里就需要 dep与watcher 双向记住对方了
        /*
        
        */
        let i = this.newSubs.length;
        while(i--){
            this.newSubs[i].depend();
        }
    }
    update(){
        if (this.lazy){
            // 依赖变化了只标记为diry, 等 computed需要值的时候判断如果是 dirty, 
            // 就执行 evaluate
            this.dirty = true;
        }else{
            queueWatcher(this);
        }
    }
    run(){
        const old = this.value;
        // 获取新的值, 注意这里会重复收集当前watcher, 因此给watcher加个id, 不让重复收集
        const value = this.get();
        this.cb.call(this.value, value, old);
    }
}

